// -*- c++ -*-
// C++11 30
#pragma once

#include <utility>
#include "bind.hh"

namespace std
{
  class thread
  {
  public:
    class id
    {
      long thread_;

      friend class thread;
      explicit id(int thread) noexcept : thread_(thread) { }

    public:
      id() noexcept : thread_(-1) { }

      friend bool operator==(thread::id x, thread::id y) noexcept
      {
        return x.thread_ == y.thread_;
      }

      friend bool operator!=(thread::id x, thread::id y) noexcept
      {
        return x.thread_ != y.thread_;
      }

      friend bool operator<(thread::id x, thread::id y) noexcept
      {
        return x.thread_ < y.thread_;
      }

      friend bool operator<=(thread::id x, thread::id y) noexcept
      {
        return x.thread_ <= y.thread_;
      }

      friend bool operator>(thread::id x, thread::id y) noexcept
      {
        return x.thread_ > y.thread_;
      }

      friend bool operator>=(thread::id x, thread::id y) noexcept
      {
        return x.thread_ >= y.thread_;
      }

      static id __get_this_thread_id() noexcept;
    };

  private:
    id id_;

    class vrun
    {
    public:
      virtual ~vrun() { }
      virtual void run() = 0;
    };

    template<class CB>
    class vruncb : public vrun
    {
      CB cb_;
    public:
      vruncb(CB&& cb) : cb_(forward<CB>(cb)) { }
      void run() override
      {
        cb_();
      }
    };

    template<class CB>
    static vrun *make_vrun(CB&& cb)
    {
      return new vruncb<CB>(forward<CB>(cb));
    }

    static void *vrun_wrapper(void *opaque);

    thread(vrun *r);

  public:
    thread() noexcept { }

    template<class F, class ...Args>
    explicit thread(F&& f, Args&&... args)
      : thread(make_vrun(bind_simple(forward<F>(f), forward<Args>(args)...)))
    { }

    thread(thread&& x) noexcept : id_(x.id_)
    {
      x.id_ = id();
    }

    ~thread();

    thread& operator=(thread&& x) noexcept;

    void swap(thread& x) noexcept
    {
      thread tmp(move(*this));
      *this = move(x);
      x = move(tmp);
    }

    bool joinable() const noexcept
    {
      return get_id() != id();
    }

    void join();

    // void detach();

    id get_id() const noexcept
    {
      return id_;
    }

    static int hardware_concurrency() noexcept
    {
      const int NCPU = 8;
      return NCPU;
    }
  };

  static inline void swap(thread& x, thread& y) noexcept
  {
    x.swap(y);
  }

  namespace this_thread
  {
    static inline thread::id get_id() noexcept
    {
      return thread::id::__get_this_thread_id();
    }
  }
}
